#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "queue_generation.h"

//обрабатывает ошибки при работе с базой
void db_error(sqlite3 *db) {
    int errcode = sqlite3_errcode(db);
    const char *errmsg = sqlite3_errmsg(db);
    fprintf(stderr, "Database error %d: %s\n", errcode, errmsg);
}

//создать таблицы если они не сущетсвуют
int create_tables_if_not_exist(sqlite3 *db) {
    static const char *create_tables =
        //таблица путей
        "CREATE TABLE IF NOT EXISTS paths("
            "id INTEGER PRIMARY KEY AUTOINCREMENT,"
            "path TEXT UNIQUE"
        ");"
        //таблица тегов
        "CREATE TABLE IF NOT EXISTS tags("
            "id INTEGER PRIMARY KEY AUTOINCREMENT,"
            "tag TEXT UNIQUE"
        ");"
        //развязочная таблица пути<->теги
        "CREATE TABLE IF NOT EXISTS tags_paths("
            "tag_id INTEGER,"
            "path_id INTEGER,"
            "PRIMARY KEY(tag_id, path_id)"
        ");";

    return sqlite3_exec(db, create_tables, NULL, NULL, NULL);
}

//создаёт новый текстовый вектор
char **alloc_text_vector(const size) {
    return (char **) malloc(sizeof(char *) * size);
}

//расширяет текстовый вектор размера size до размера new_size
char **expand_text_vector(char ***vector, int *size, const int new_size) {
    *vector = (char **) realloc(*vector, sizeof(char *) * new_size);
    *size = new_size;

    return *vector;
}

//возвращает вектор строк порождённых запросом queue
int get_text_lines(sqlite3 *db, char ***text_vector, const char *queue) {
    //размер вектора для считываемых записей
    int text_vector_size = 1;
    //вектор результатов
    *text_vector = alloc_text_vector(text_vector_size);
    //количество записей в векторе
    int text_lines = 0;
    
    //код завершения операции с базой
    int rc; 
    sqlite3_stmt *statement;

    char *cur_line;
    char *new_line;
    do {
        
        rc = sqlite3_prepare_v2(db, queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }
        
        while((rc = sqlite3_step(statement)) == SQLITE_ROW) {
            cur_line = (char *) sqlite3_column_text(statement, 0);
            new_line = alloc_string(strlen(cur_line));
            strcpy(new_line, cur_line);
            if(text_lines == (text_vector_size - 1)) {
                expand_text_vector(text_vector,
                        &text_vector_size,
                        text_vector_size * 2);
            }

            (*text_vector)[text_lines] = new_line;
            text_lines++;
        }

        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    (*text_vector)[text_lines] = NULL;
    return rc;
}

//возвращает вектор путей отмеченных тегами tags
int get_paths_with_tags(sqlite3 *db, char ***paths, char *tags[]) {
    int rc;

    //генерация запроса
    char *tagged_file_select_queue = new_queue(tags);
    // результирующий вектор
    rc = get_text_lines(db, paths, tagged_file_select_queue);

    free(tagged_file_select_queue);
    return rc;
}

//возвращает вектор тегов имеющихся в базе
int get_all_tags(sqlite3 *db, char ***tags) {
    int rc;

    const char *all_tags_queue =
        "SELECT tag FROM tags";

    rc = get_text_lines(db, tags, all_tags_queue);
    return rc;
}

//добавляет новый тег в таблицу тегов, если тег уже существует он игнорируется
int add_tag_if_not_exists(sqlite3 *db, const char *tag) {
    const char *insert_tag_queue =
        "INSERT OR IGNORE INTO tags(tag) VALUES(?)";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, insert_tag_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, tag, -1, SQLITE_STATIC);

        rc = sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

int add_tags_if_not_exist(sqlite3 *db, const char *tags[]) {
    int rc = SQLITE_OK;

    int i;
    for(i = 0; tags[i] != NULL && rc == SQLITE_OK; i++) {
        rc = add_tag_if_not_exists(db, tags[i]);
    }

    return rc;
}

int delete_tag(sqlite3 *db, const char *tag) {
    const char *delete_tag_queue =
        //удаляем все связи с удаляемым тегом
        "DELETE FROM tags_paths "
            "WHERE tag_id = (SELECT id FROM tags WHERE tag = ?);"
        //удаляем сам тег
        "DELETE FROM tags WHERE tag = ?;";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, delete_tag_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, tag, -1, SQLITE_STATIC);
        sqlite3_bind_text(statement, 2, tag, -1, SQLITE_STATIC);

        sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

int rename_tag(sqlite3 *db, const char *old_name, const char *new_name) {
    const char *rename_tag_queue =
        "UPDATE tags SET tag = ? WHERE tag = ?";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, rename_tag_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, new_name, -1, SQLITE_STATIC);
        sqlite3_bind_text(statement, 2, old_name, -1, SQLITE_STATIC);

        sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

//добавляет новый путь, в таблицу путей
//если путь в таблице уже существует, он игнорируется
int add_path_if_not_exists(sqlite3 *db, const char *path) {
    const char *insert_path_queue =
        "INSERT OR IGNORE INTO paths(path) VALUES(?)";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, insert_path_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, path, -1, SQLITE_STATIC);

        sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

int delete_path(sqlite3 *db, const char *path) {
    const char *delete_path_queue =
        //удаляем все связи с удаляемым путём
        "DELETE FROM tags_paths "
            "WHERE path_id = (SELECT id FROM paths WHERE path = ?);"
        //удаляем сам путь
        "DELETE FROM paths WHERE path = ?;";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, delete_path_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, path, -1, SQLITE_STATIC);
        sqlite3_bind_text(statement, 2, path, -1, SQLITE_STATIC);

        sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

int add_tag_path_link(sqlite3 *db, const char *tag, const char *path) {
    const char *insert_tag_path_queue =
        "INSERT INTO tags_paths(tag_id, path_id) VALUES("
            "(SELECT id FROM tags WHERE tag = ?),"
            "(SELECT id FROM paths WHERE path = ?)"
        ")";

    sqlite3_stmt *statement;
    int rc;

    do {

        rc = sqlite3_prepare(db, insert_tag_path_queue, -1, &statement, NULL);
        if(rc != SQLITE_OK) {
            return rc;
        }

        sqlite3_bind_text(statement, 1, tag, -1, SQLITE_STATIC);
        sqlite3_bind_text(statement, 2, path, -1, SQLITE_STATIC);

        sqlite3_step(statement);
        rc = sqlite3_finalize(statement);
    } while(rc == SQLITE_SCHEMA);

    return rc;
}

int add_path_tags_links(sqlite3 *db, const char *path, const char *tags[]) {
    int i;

    int rc = add_path_if_not_exists(db, path);
    if(rc != SQLITE_OK) {
        return rc;
    }

    rc = add_tags_if_not_exist(db, tags);
    for(i = 0; tags[i] != NULL && rc == SQLITE_OK; i++) {
        rc = add_tag_path_link(db, tags[i], path);
    }

    return rc;
}


int main(int argc, char *argv[]) {
    
    int i;
    int rc;
    sqlite3 *db;
    char **paths;

    if(argc < 2) {
        return -1;
    }

    if(sqlite3_open(argv[1], &db) != SQLITE_OK) {
        fprintf(stderr, "sqlite_open: %s\n", sqlite3_errmsg(db));
        return -1;
    }
    printf("sqlite_open: OK\n");

    if((rc = create_tables_if_not_exist(db)) != SQLITE_OK) {
        fprintf(stderr, "create_tables_if_not_exist: %s\n", sqlite3_errmsg(db));
        return -1;
    }
    printf("create_tables_if_not_exist: OK\n");

    if(argc < 3) {
        sqlite3_close(db);
        return 0;
    }

    get_paths_with_tags(db, &paths, argv + 2);
    for(i = 0; paths[i] != NULL; i++) {
        printf("%s\n", paths[i]);
    }
    /*
    for(i = 2; argv[i] != NULL && rc == SQLITE_OK; i++) {
        printf("adding: %s\n", argv[i]);
        rc = add_path_if_not_exists(db, argv[i]);
        if(rc != SQLITE_OK) {
            fprintf(stderr, "add_path: %s\n", sqlite3_errmsg(db));
            return -1;
        }
        
        printf("add_path: OK\n");
    }*/

    sqlite3_close(db);

    return 0;
}


